package biolabs.model

import biolabs.core.BioContext
import biolabs.core.BioObject
import biolabs.core.Genomic
import biolabs.core.WrappedDelegate
import biolabs.model.abscretion.AbscretionDelegate
import biolabs.model.abscretion.BorderAbscretor
import biolabs.model.division.DivisibleByVolume
import biolabs.model.division.DivisionDelegate
import biolabs.model.division.FloodFillSpaceDivider
import biolabs.model.division.LastGrowthDivisionPlaneDeterminator
import biolabs.model.division.PlanarDivisionEndsDeterminator
import biolabs.model.growth.GrowableByReceptorDensity
import biolabs.model.growth.GrowthDelegate
import biolabs.model.motion.ChemotaxicMovable
import biolabs.model.motion.MotionDelegate
import biolabs.space.{ContinuousPosition, Continuous2DPosition, Continuous3DPosition, DiscretePosition, Discrete2DPosition, Discrete3DPosition}
import biolabs.space.GridShape
import biolabs.space.GridShapedObject
import biolabs.visual.VisualizationConfiguration

object Cell {
	trait Genome
	extends BorderAbscretor.Genome
	with DivisibleByVolume.Genome
	with GrowableByReceptorDensity.Genome
	with ChemotaxicMovable.Genome

	class Cell2D(g: Genome)
	extends Cell[Cell2D, Discrete2DPosition, Continuous2DPosition](g) {
		def this(g: Genome, shape: GridShape[Discrete2DPosition, Continuous2DPosition], cs: Any) {
			this(g)
			init(shape, cs)
		}
		override def newInstance(): Cell2D = {
			new Cell2D(genome)
		}
		override def canVisualize[R](config: VisualizationConfiguration[R]) =
			config.get(this).isDefined
		override def visualize[R](config: VisualizationConfiguration[R]) =
			config.get(this) match {
				case Some((visualizers, augmentations)) =>
					// Cell has no children,
					// thus there can be no augmentations
					assert(augmentations == Nil)

					Some(config.method.combine(visualizers map { _.visualize(this) }))

				case None =>
					None
			}
	}

	class Cell3D(g: Genome)
	extends Cell[Cell3D, Discrete3DPosition, Continuous3DPosition](g) {
		def this(g: Genome, shape: GridShape[Discrete3DPosition, Continuous3DPosition], cs: Any) {
			this(g)
			init(shape, cs)
		}
		override def newInstance(): Cell3D = {
			new Cell3D(genome)
		}
		override def canVisualize[R](config: VisualizationConfiguration[R]) =
			config.get(this).isDefined
		override def visualize[R](config: VisualizationConfiguration[R]) =
			config.get(this) match {
				case Some((visualizers, augmentations)) =>
					// Cell has no children,
					// thus there can be no augmentations
					assert(augmentations == Nil)

					Some(config.method.combine(visualizers map { _.visualize(this) }))

				case None =>
					None
			}
	}
}

abstract class Cell[
	Self <: Cell[Self, P, C],
	P <: DiscretePosition[P, C],
	C <: ContinuousPosition[C, P]
](g: Cell.Genome)
extends BioObject
with GridShapedObject[P, C]
with BorderAbscretor[Self, P, C]
with CumulationStateful[Self, P]
with Genomic[Cell.Genome]
with DivisibleByVolume[Self, P, C]
with FloodFillSpaceDivider[P, C]
with PlanarDivisionEndsDeterminator[P, C]
with LastGrowthDivisionPlaneDeterminator[P, C]
with GrowableByReceptorDensity[Self, P, C]
with ChemotaxicMovable[Self, P, C]
{

self: Self =>

	override val genome = g

	def this(g: Cell.Genome, shape: GridShape[P, C], cs: Any) {
		this(g)
		init(shape, cs)
	}

	def init(shape: GridShape[P, C], cs: Any) {
		super[GridShapedObject].init(shape)
		super[CumulationStateful].init(cs)
		super[BorderAbscretor].init()
	}

	override def init() {
		throw new AbstractMethodError("Conflicting method init()")
	}
	override def proceed(fromTime: Long, toTime: Long, ctx: BioContext) {
		super[BorderAbscretor].proceed(fromTime, toTime, ctx)
	}
	override def abscretionDelegate_wrapped(substance: Any) = {
		abscretionDelegate(substance) match {
			case Some(ad) => Some(new WrappedDelegate(ad, this) {
				override def commit() {
					Cell.super[BorderAbscretor].handleAbscretion(ad)
				}
			})
			case None => None
		}
	}
	override def growthDelegate_wrapped() = {
		growthDelegate match {
			case Some(gd) => Some(new WrappedDelegate(gd, this) {
				override def commit() {
					Cell.super[GridShapedObject].handleGrowth(gd)
					Cell.super[BorderAbscretor].handleGrowth(gd)
					Cell.super[LastGrowthDivisionPlaneDeterminator].handleGrowth(gd)
				}
			})
			case None => None
		}
	}
	override def divisionDelegate_wrapped() = {
		divisionDelegate match {
			case Some(dd) => Some(new WrappedDelegate(dd, this) {
				override def commit() {
					Cell.super[DivisibleByVolume].handleDivision(dd)
					Cell.super[BorderAbscretor].handleDivision(dd)
					Cell.super[CumulationStateful].handleDivision(dd)
				}
			})
			case None => None
		}
	}
	override def motionDelegate_wrapped() = {
		motionDelegate match {
			case Some(md) => Some(new WrappedDelegate(md, this) {
				override def commit() {
					Cell.super[BorderAbscretor].handleMotion(md)
				}
			})
			case None => None
		}
	}
	override def close() {}

	override def handleAbscretion(ad: AbscretionDelegate[P]) {
		throw new AbstractMethodError("Explicit call to a handler method is not permitted.")
	}
	override def handleGrowth(gd: GrowthDelegate[P]) {
		throw new AbstractMethodError("Explicit call to a handler method is not permitted.")
	}
	override def handleDivision(dd: DivisionDelegate[P, Self]) {
		throw new AbstractMethodError("Explicit call to a handler method is not permitted.")
	}
	override def handleMotion(md: MotionDelegate[P]) {
		throw new AbstractMethodError("Explicit call to a handler method is not permitted.")
	}

}
